// Thud - a dynamic object-oriented language
// Copyright (C) 2009,2010 David H. Hovemeyer <david.hovemeyer@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

/*
 * VM_Interpreter.h
 *
 *  Created on: Jan 1, 2010
 *      Author: dhovemey
 */

#ifndef VM_INTERPRETER_H_
#define VM_INTERPRETER_H_

#include <cstddef>
#include <string>
#include <map>
#include <vector>
#include "vm/CodeBase.h"
#include "interp/VM_Class.h"
#include "interp/VM_Method.h"
#include "interp/VM_Arena.h"
class Class;
class Method;

// FIXME: temporary hack
const size_t VM_INTERPRETER_DEFAULT_MAX_OBJS = 8192;

/**
 * The interpreter for executing compiled bytecode.
 * Note that interpreter loop is actually in VM_Thread.
 * The instance of this class represents all of the "global"
 * state of the interpreter.
 */
class VM_Interpreter {
private:
	typedef std::map<Class *, VM_Class *> VM_ClassMap;
	typedef std::map<std::wstring, VM_Object *> VM_SymbolMap;
	typedef std::map<std::wstring, VM_Object *> VM_StringMap;
	typedef std::vector<VM_Object *> VM_Int32Map;
	typedef std::vector<VM_Class *> VM_ClassList;
	typedef std::map<VM_Class *, VM_Object *> VM_ModuleRefMap;
	typedef std::vector<VM_Thread *> VM_ThreadList;

	CodeBase m_codeBase;
	VM_ClassMap m_vmClassMap;
	VM_SymbolMap m_vmSymbolMap;
	VM_StringMap m_vmStringMap;
	VM_Int32Map m_vmInt32Map;
	VM_Int32Map m_vmCharacterMap;
	VM_Object *m_vmBooleanList[2];
	VM_ClassList m_vmClassList;
	VM_ModuleRefMap m_vmModuleRefMap;
	VM_Object *m_nil;

	void *m_rtLibHandle;

#ifndef NDEBUG
	bool m_trace;
	bool m_traceGC;
#endif

	VM_Arena *m_space;
	VM_ThreadList m_threadList;

	VM_Interpreter(const VM_Interpreter &);
	VM_Interpreter& operator=(const VM_Interpreter &);

public:
	/**
	 * Constructor.
	 *
	 * @param maxObjs maximum number of objects which can be allocated (i.e., heap size)
	 */
	VM_Interpreter(size_t maxObjs = VM_INTERPRETER_DEFAULT_MAX_OBJS);

	/** Destructor. */
	~VM_Interpreter();

	/**
	 * Set the classpath that will be used to load referenced classes.
	 *
	 * @param clsPathStr the classpath string
	 */
	void setClassPath(const std::string &clsPathStr);

	/**
	 * Set the program name (i.e., argv[0] of the main function).
	 * This information is used to locate the Thud home (installation)
	 * directory.
	 *
	 * @param progName the program name
	 */
	void setProgName(const std::string &progName);

#ifndef NDEBUG
	void setTrace(bool trace) { m_trace = trace; }
	void setTraceGC(bool traceGC) { m_traceGC = traceGC; }
#endif

	/**
	 * Get the CodeBase used by the interpreter.
	 */
	CodeBase &getCodeBase() { return m_codeBase; }

	/**
	 * Interpret the main method of a class.
	 *
	 * @param a Class
	 * @param the main Method of the Class
	 * @return 0 if main method returned successfully,
	 *           non-zero if main method returned with an error code
	 */
	int interpret(Class *mainClass, Method *mainMethod);

	/**
	 * Create an instance of specified class.
	 *
	 * @param clsId  class id of the class to instantiate
	 * @param thread the VM_Thread initiating the creation of the object
	 */
	VM_Object *instantiate(ClassId clsId, VM_Thread *thread);

	/**
	 * Get the VM_Object representing specified symbol.
	 *
	 * @param name name of symbol
	 * @param thread the VM_Thread requesting the symbol
	 * @return the symbol VM_Object
	 */
	VM_Object *getVMSymbol(const std::wstring &name, VM_Thread *thread);

	/**
	 * Get the VM_Class which "wraps" the given bytecode Class.
	 *
	 * @param cls a bytecode Class
	 * @return the VM_Class wrapping the Class
	 */
	VM_Class *getVMClass(Class *cls);

	/**
	 * Get an interned VM_String.
	 *
	 * @param value the value of the string
	 * @param thread the VM_Thread requesting the string
	 * @return the string VM_Object
	 */
	VM_Object *getInternedVMString(const std::wstring &value, VM_Thread *thread);

	/**
	 * Get a non-interned (collectable) VM_String.
	 *
	 * @param value the value of the string
	 * @param thread the VM_Thread requesting the string
	 * @return the string VM_Object
	 */
	VM_Object *getVMString(const std::wstring &value, VM_Thread *thread);

	/**
	 * Get the VM_Object representing specified integer value.
	 *
	 * @param value value of the integer
	 * @param thread the VM_Thread requesting the integer
	 * @return the Int32 VM_Object
	 */
	VM_Object *getVMInt32(int32_t value, VM_Thread *thread);

	/**
	 * Get the VM_Object representing specified character value.
	 *
	 * @param c value of the character
	 * @param thread the VM_Thread requesting the character
	 * @return the Character VM_Object
	 */
	VM_Object *getVMCharacter(wchar_t c, VM_Thread *thread);

	/**
	 * Get the VM_Object representing specified boolean value.
	 *
	 * @param value the boolean value
	 * @param thread the VM_Thread requesting the boolean
	 * @return the Boolean VM_Object
	 */
	VM_Object *getVMBoolean(bool value);

	/**
	 * Look up a VM_Class by its class id.
	 *
	 * @param clsId a class id
	 * @return VM_Class with the specified class id
	 */
	VM_Class *lookupVMClassById(ClassId clsId);

	/**
	 * Get the VM_Object which is the single instantiation of
	 * the given VM_Class (which must be a module).
	 *
	 * @param cls a VM_Class (which is a module)
	 * @param thread the VM_Thread requesting the module reference
	 * @return VM_Object which is the instantiation of the requested module
	 */
	VM_Object *getModuleRef(VM_Class *cls, VM_Thread *thread);

	/**
	 * Get the reference to the nil object.
	 */
	VM_Object *getNil() { return m_nil; }

	/**
	 * Check if given VM_Object belongs to given class.
	 *
	 * @param obj a VM_Object
	 * @param id  class id of a class
	 * @return true if the VM_Object belongs to the class, false otherwise
	 */
	bool isType(VM_Object *obj, ClassId id) { return obj->getClassId() == id; }

	/**
	 * Comparison method for VM_Objects with different types.
	 * This is necessary because the compareTo() method in all Thud
	 * objects is intended to provide a total order for all objects,
	 * including objects with different types.
	 */
	int compareDifferentTypes(VM_Object *left, VM_Object *right);

	/**
	 * Get specified VM_MagicMethod in a class.
	 *
	 * @param clsName name of a class containing a magic method
	 * @param methodName name of the magic method
	 * @return resolved VM_MagicMethod
	 */
	VM_MagicMethod *getMagicMethod(const std::wstring &clsName, const std::wstring &methodName);
	VM_Class_Init_Func *getMagicClassInitFunc(const std::wstring &clsName);

	/**
	 * Register a VM_Thread with the interpreter.
	 */
	void registerThread(VM_Thread *thread);

	/**
	 * Unregister a VM_Thread from the interpreter.
	 */
	void unregisterThread(VM_Thread *thread);

	/**
	 * Mark all VM_Objects reachable from the global interpreter state.
	 */
	void markReachableObjects();

private:
	void init(VM_Thread *thread);
	VM_Object *internIntegerValue(
			VM_Int32Map &map, int32_t min, int32_t max, int32_t value, ClassId clsId, VM_Thread *thread);
	static std::string mangle(const std::wstring &name);
	void *lookupMagicMethod(const std::string &name);
};

#endif /* VM_INTERPRETER_H_ */
